using System;
using System.IO;
using System.Text;
using System.Threading;
using ICSharpCode.SharpZipLib;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;

namespace ScrewTurn.Wiki
{
    /// <summary>
    /// Allows to create a Zip File using #ZipLib.
    /// </summary>
    public static class ZipFile
    {
        /// <summary>
        /// Creates a Zip File.
        /// </summary>
        /// <param name="files">The Files to include in the Zip File.</param>
        /// <param name="baseDir">The Base directory of the Files (it will be removed from their full path).</param>
        /// <param name="destinationStream">The destination Stream.</param>
        /// <param name="level">The compression level (0 min - 9 max).</param>
        public static void Create(string[] files, string baseDir, Stream destinationStream, int level)
        {
            ZipOutputStream output = new ZipOutputStream(destinationStream);
            output.SetLevel(level);

            if (!baseDir.EndsWith("\\")) baseDir += "\\";
            Crc32 crc32 = new Crc32();

            string errors = "";
            int errCount = 0;

            ZipEntry entry = null;
            FileStream input = null;
            for (int i = 0; i < files.Length; i++)
            {
                input = null;
                files[i] = files[i].Substring(baseDir.Length);
                entry = new ZipEntry(files[i]);
                for (int k = 0; k < Settings.FileAccessTries; k++)
                {
                    try
                    {
                        input = new FileStream(baseDir + files[i], FileMode.Open, FileAccess.Read, FileShare.Read);
                        break;
                    }
                    catch
                    {
                        input = null;
                        Thread.Sleep(Settings.FileAccessTryDelay);
                    }
                }
                if (input == null)
                {
                    //throw new IOException("Unable to open file: " + baseDir + files[i]);
                    errors += "Unable to open file: " + baseDir + files[i] + "\r\n";
                    errCount++;
                    continue;
                }

                int read = 0;
                byte[] buff = new byte[65536];
                crc32.Reset();

                output.PutNextEntry(entry);

                do
                {
                    read = input.Read(buff, 0, buff.Length);
                    output.Write(buff, 0, read);
                    crc32.Update(buff, 0, read);
                } while (read > 0);

                input.Close();

                entry.Crc = crc32.Value;
                FileInfo fInfo = new FileInfo(baseDir + files[i]);
                entry.DateTime = fInfo.LastWriteTime;
                entry.Size = fInfo.Length;
            }

            if (errCount == 0) errors = "Backup completed successfully at " + DateTime.Now.ToString("R") + ".";
            else
                errors += "Backup completed with " + errCount.ToString() + " errors at " + DateTime.Now.ToString("R") +
                          ".";
            byte[] e = UTF8Encoding.UTF8.GetBytes(errors);
            entry = new ZipEntry("ZipBackupReport.txt");
            crc32.Reset();
            output.PutNextEntry(entry);
            output.Write(e, 0, e.Length);
            crc32.Update(e);
            entry.Crc = crc32.Value;
            entry.DateTime = DateTime.Now;
            entry.Size = e.Length;

            output.Finish();
            output.Close();
        }

        /// <summary>
        /// Creates a Zip File.
        /// </summary>
        /// <param name="files">The Files to include in the Zip File.</param>
        /// <param name="baseDir">The Base directory of the Files (it will be removed from their full path).</param>
        /// <param name="destination">The destination Zip File.</param>
        /// <param name="level">The compression level (0 min - 9 max).</param>
        public static void Create(string[] files, string baseDir, string destination, int level)
        {
            Create(files, baseDir, File.Create(destination), level);
        }
    }
}